import java.io.*;
import java.text.DecimalFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import libsvm.*;


public class EmoDetect_m3 {

	public static HashSet<String> stopwords = new HashSet<String>();//停用词（包括英文字母)
	public static HashSet<String> senti = new HashSet<String>();//情感词典
	public static HashMap<Integer,String> test = new HashMap<Integer,String>();//将过滤到路径信息的文本写入test
	public static HashMap<Integer,String> svmtest = new HashMap<Integer,String>();//存放svmtest特征文本
	public static List<String> result = new ArrayList<String>();//存放result
	public static List<String> resultlist = new ArrayList<String>();//存放result+sen;送入规则处理
	public static List<String> newresultlist = new ArrayList<String>();//规则处理后的结果
	public static List<String> txt_speech_result = new ArrayList<String>();
	public static HashMap<Integer,String> fea_senti = new HashMap<Integer,String>();
	public static HashMap<String, Double> gramIDF= new HashMap<String, Double>(); //存放所有gram和gram2的IDF
	public static HashMap<String, Integer> gramIndex= new HashMap<String, Integer>(); //存放所有gram和gram2和其index	
	public static HashMap<String, Double> txt_result = new HashMap<String, Double>(); 
	public static HashMap<String, String> speech_result = new HashMap<String, String>(); 
	public static HashMap<Integer,String> indexFile = new HashMap<Integer,String>();//将过滤到路径信息的文本写入test
	
	DecimalFormat fnum = new DecimalFormat("##0.000"); 
	
	public void readDic() throws Exception{//生成stopwords, senti, gramIDF, gramIndex, gram2IDF，gram2Index
		
		InputStreamReader read = new InputStreamReader(new FileInputStream("dictionary_g1_g2_senti_0720"),"utf-8");
		BufferedReader reader = new BufferedReader(read);
		String temp ="";
		while((temp = reader.readLine()) != null){
			String[] tokens = temp.split("\t");
			String cate = tokens[0];
			String item = tokens[1];
			switch(cate){
				case "st"://代表停用词
					stopwords.add(item);
					break;
				case "se"://代表情感词
					senti.add(item);
					break;
				case "g":
					String[] gramtokens = item.split(" ");
					Integer index = Integer.valueOf(gramtokens[0]);
					String gram = gramtokens[1];
					Double gramidf = Double.valueOf(gramtokens[2]);
					gramIDF.put(gram, gramidf);
					gramIndex.put(gram, index);
					break;
			}
		}
		reader.close();
	}
	
	public String getSentiFea(String line) throws Exception{
		
		String feasenti = "";
		for(String sentiline : senti){
			String[] tokens = sentiline.split(" ");
			String index = tokens[0];
			String lex = tokens[1];
			String value = tokens[2];
			if(line.contains(lex)){
				feasenti = feasenti + index + ":" + value + " ";
			}
		}
		return feasenti;
	}
	
	public void readFile(String file) throws Exception{//读取文本，同时获取角色、情感特征
		
		InputStreamReader read = new InputStreamReader(new FileInputStream(file),"gbk");
		BufferedReader reader = new BufferedReader(read);
		String testtemp = "";
		String content = "";
		String senti = "";
		int index = 0;
		while((testtemp = reader.readLine()) != null){			
			if (testtemp.substring(0, 1).contains("/")){//过滤路径行
//			if (testtemp.contains(".wav")){//过滤路径行
//				System.err.println(testtemp);
				continue;
				
			}
			String[] tokens = testtemp.split("	");
			content = tokens[2];	
			senti = getSentiFea(content);
			test.put(index, content);	
			fea_senti.put(index, senti);
			index += 1;		
//			System.err.println(index);
		}
		reader.close();
	}
	
	public void FormateData() throws IOException{		
		
		String content = "";
		int index = 0;
		Iterator<Integer> iter = test.keySet().iterator();
		while (iter.hasNext()) {
			index = iter.next();
			content = test.get(index);
			HashMap<String, Integer> gramMap = new HashMap<String, Integer>();//gramMap存放每句话的gram和gram2信息			
			for(int i= 0; i<content.length()-1; i++){
				String gram = content.substring(i, i+1);
				if(stopwords.contains(gram)==false && gram.length()>0 && gram.trim().isEmpty()==false){
					if(gramMap.containsKey(gram)){
						gramMap.put(gram, gramMap.get(gram)+1);
					}else{
						gramMap.put(gram, 1);
					}
				}
				String gram2 = content.substring(i, i+2);//word为2gram
				if(stopwords.contains(gram2)==false && gram2.length()>0 && gram2.trim().isEmpty()==false){
					if(gramMap.containsKey(gram2)){
						gramMap.put(gram2, gramMap.get(gram2)+1);
					}else{
						gramMap.put(gram2, 1);
					}			
				}
			}			
			HashMap<Integer, Double> gramTfIdf = new HashMap<Integer, Double>();
			Iterator<String> grammap = gramMap.keySet().iterator();
			while(grammap.hasNext()){
				String gram = grammap.next();
				if(gramIndex.containsKey(gram) && gramIDF.containsKey(gram)){
					double tf = (double)(gramMap.get(gram)) / (content.length() - 1);
					double idf = gramIDF.get(gram);
					double tfidf = tf * idf ;
					gramTfIdf.put(gramIndex.get(gram), tfidf);
				}
			}
			String fea_word = "";
		    Object[] key =  gramTfIdf.keySet().toArray();
		    Arrays.sort(key); 
		    for(int i = 0; i<key.length; i++)  {    
		    	fea_word = fea_word +  String.valueOf(key[i]) + ":" +  String.valueOf(gramTfIdf.get(key[i])) + " ";    		    	
		    } 
//			System.out.println(fea_word);
//		    String fea_all = fea_role.get(index) + " " + fea_senti.get(index) + fea_word;
//		    String fea_all = fea_senti.get(index) + fea_word;
		    String fea_all = fea_word + fea_senti.get(index);
//		    System.out.println(fea_senti.get(index));
		    svmtest.put(index, fea_all);
		}
		System.out.println("Feature Completed. ");
	}
	
	public String predict(String line, svm_model model, int predict_probability) throws IOException{
		
		String output = "";
		int svm_type=svm.svm_get_svm_type(model);
		int nr_class=svm.svm_get_nr_class(model);
		double[] prob_estimates=null;
		
		if(predict_probability == 1){
			int[] labels=new int[nr_class];
			svm.svm_get_labels(model,labels);
			prob_estimates = new double[nr_class];
		}
		
		StringTokenizer st = new StringTokenizer(line," \t\n\r\f:");
		
		int m = st.countTokens()/2;
		svm_node[] x = new svm_node[m];
		for(int j=0;j<m;j++){
			x[j] = new svm_node();
			x[j].index = Integer.parseInt(st.nextToken());
			x[j].value = Double.valueOf(st.nextToken()).doubleValue();
		}
		
		double v;
		if (predict_probability==1 && (svm_type==svm_parameter.C_SVC || svm_type==svm_parameter.NU_SVC)){
			v = svm.svm_predict_probability(model,x,prob_estimates);
			output = v+" ";
			for(int j=0;j<nr_class;j++)
				output= output + prob_estimates[j]+" ";
		}else{
			v = svm.svm_predict(model,x);
			output= v+"\n";
		}		
		return output;
	}
	
	public List<String> multipredict(List<Map.Entry<Integer, String>> list, Integer treadcount) throws Exception{
		
		final svm_model model = svm.svm_load_model("model_g1_g2_senti_0720");
		HashMap<Integer, String> resultTemp = new HashMap<Integer, String>();//存放预测结果
		List<String> resultLs = new ArrayList<String>();//存放预测结果
		    
		int threadCounts = treadcount;//此处定义使用的线程数  
		ExecutorService exec=Executors.newFixedThreadPool(threadCounts);//根据定义的线程数目进行开辟线程
		List<Callable<HashMap<Integer,String>>> callList=new ArrayList<Callable<HashMap<Integer,String>>>();//生成很大的List
		
		int len=list.size()/threadCounts;//根据线程数目，平均分割List，这是每段sublist的大小		
		if(len==0){//List中的数量没有线程数多（很少存在）
			threadCounts=list.size();//采用一个线程处理List中的一个元素
			len=list.size()/threadCounts;//重新平均分割List
		}//此处是为了防止线程数目大于list的大小，可不用管
		for(int i=0;i<threadCounts;i++){//这个for循环是为了启动各个线程，并且定义每个线程要处理的内容						
			final List<Entry<Integer, String>> subList;
			if(i==threadCounts-1){
				subList=list.subList(i*len,list.size());
			}else{
				subList=list.subList(i*len, len*(i+1)>list.size()?list.size():len*(i+1));
			}//此处if-else是为了在for循环中均分前面定义的list
			callList.add(new Callable<HashMap<Integer,String>>(){
				public HashMap<Integer,String> call() throws Exception {
//					List<Entry<Integer, String>> alist = new ArrayList<Entry<Integer, String>>();
					HashMap<Integer,String> alistMap = new HashMap<Integer, String>();
					for(Entry<Integer, String> i:subList){
						int index = i.getKey();
						String line = i.getValue();
						alistMap.put(index, predict(line, model, 1));//对每一句话进行预测
					}
					return alistMap;//此处将上述处理得到的结果进行返回，并保存到callList中
				}
			});
		}
		//等待所有的for循环处理完毕，将每个线程中的结果集合进行汇总
		List<Future<HashMap<Integer, String>>> futureList=exec.invokeAll(callList);
		for(Future<HashMap<Integer, String>> future:futureList){
			resultTemp.putAll(future.get());
		}		
		Object[] key =  resultTemp.keySet().toArray();//将结果进行排序，以免组合sublist时乱序
		Arrays.sort(key); 
	    for(int i = 0; i<key.length; i++)  {    
	    	resultLs.add(resultTemp.get(key[i])); 
//	    	System.out.println(resultTemp.get(key[i]));
	    }		
		exec.shutdown();//结束线程集合
		
		return resultLs;
	}
	
	public void Predict(Integer treadcount) throws Exception{	
			
		List<Map.Entry<Integer, String>> svmtestList = new ArrayList<Map.Entry<Integer, String>>(svmtest.entrySet());
		result = multipredict(svmtestList, treadcount);	
	}
		
	public void conbineTxtResult(String path) throws Exception{		
		
		InputStreamReader read = new InputStreamReader(new FileInputStream(path),"gbk");
		BufferedReader reader = new BufferedReader(read);
		String testtemp = "";
		int i = 0;
		while((testtemp = reader.readLine()) != null){	
			if (testtemp.substring(0, 1).contains("/")){
//			if (testtemp.contains(".wav")){
			resultlist.add(testtemp + '\n');
			continue;
			}
			String re = result.get(i);
			double P1 = Double.valueOf(re.split(" ")[1]);
//			double P2 = Double.valueOf(re.split(" ")[2]);
			resultlist.add(testtemp + "\t" + "[Angry]" +"\t" + fnum.format(P1) + "\n");
/*			String lable = re.split(" ")[0];
			switch(lable){
			case "0.0":	
				resultlist.add(testtemp + "\t" + "[Angry]" +"\t" + fnum.format(P1) + "\n");
				break;
			case "1.0":
				resultlist.add(testtemp + "\t" + "[Neu]" +"\t" + fnum.format(P2) + "\n");
				break;
			}*/
			i = i + 1;
		}	
		reader.close();	
	}
	
	public void addLingRule(String outpath) throws IOException {//加入语言规则
		
		File file = new File(outpath);
		Writer writer = new OutputStreamWriter(new FileOutputStream(file),"gbk");//将结果写入指定路径
		
		for(int i=0; i<resultlist.size(); i++){
			String newresultstring = resultlist.get(i);
			//规则1：凡是坐席说的“投诉”，词句均判断为中性
/*			if(resultlist.get(i).contains("投诉") && resultlist.get(i).contains("	坐席")){
				String[] tokens = resultlist.get(i).split("\t");
				tokens[4] = "[Neu]";
				tokens[5] = "0.999";
				newresultstring = tokens[0] + "\t" + tokens[1] + "\t" + tokens[2] + "\t" + tokens[3] + "\t" + tokens[4] + "\t" + tokens[5].trim() + "\n";			
			} 
			//规则2：若是坐席被判断为Angry，但其的概率小于0.9，则将其强行改为中性
			if (resultlist.get(i).contains("	[Angry]") && resultlist.get(i).contains("	坐席") && Float.valueOf(resultlist.get(i).split("\t")[5])< 0.9) {
				String[] tokens = resultlist.get(i).split("\t");
				tokens[4] = "[Neu]";
				tokens[5] = "0.499";
				newresultstring = tokens[0] + "\t" + tokens[1] + "\t" + tokens[2] + "\t" + tokens[3] + "\t" + tokens[4] + "\t" + tokens[5].trim() + "\n";			
			}
			//规则3：大于0.75
			if (resultlist.get(i).contains("	[Angry]") && Float.valueOf(resultlist.get(i).split("\t")[5])< 0.75) {
				String[] tokens = resultlist.get(i).split("\t");
				tokens[4] = "[Neu]";
				tokens[5] = "0.499";
				newresultstring = tokens[0] + "\t" + tokens[1] + "\t" + tokens[2] + "\t" + tokens[3] + "\t" + tokens[4] + "\t" + tokens[5].trim() + "\n";			
			}*/
			//规则4：凡是客户说的“投诉”，词句均判断为负面
			if(resultlist.get(i).contains("投诉") && resultlist.get(i).contains("	客户")){
				String[] tokens = resultlist.get(i).split("\t");
				tokens[4] = "[Angry]";
				tokens[5] = "0.999";
				newresultstring = tokens[0] + "\t" + tokens[1] + "\t" + tokens[2] + "\t" + tokens[3] + "\t" + tokens[4] + "\t" + tokens[5].trim() + "\n";			
			}
			newresultlist.add(newresultstring);
			writer.write(newresultstring);
		}
		writer.close();
	}
	
	
	public void writeResult(String outpath) throws Exception{
		
		File tetsfile = new File(outpath);
		Writer testwriter = new OutputStreamWriter(new FileOutputStream(tetsfile),"gbk");//将结果写入指定路径
		
		for(int i=0; i<newresultlist.size(); i++){
			testwriter.write(newresultlist.get(i));
		}
		testwriter.close();	
	}
		
	/***************基于segment结果得到dialogue结果******************///txt_speech_result
	public void dialogue_score(String dia_outpath) throws Exception {
		
		File tetsfile = new File(dia_outpath);
		Writer testwriter = new OutputStreamWriter(new FileOutputStream(tetsfile),"gbk");//将结果写入指定路径
		
		List<Integer> wavname_ls = new ArrayList<Integer>();
		for (int i=0; i<newresultlist.size(); i++) {			
			if (newresultlist.get(i).substring(0, 1).contains("/")){
//			if (newresultlist.get(i).contains(".wav")){
				wavname_ls.add(i);								
			} 
		}
		for(int j=0; j< wavname_ls.size()-1; j++) {
			float count = 0;
			int start = wavname_ls.get(j);
			int end = wavname_ls.get(j+1);
			for(int k= start+1; k<end; k++){
				String label = newresultlist.get(k).split("\t")[4];
				if (label.contains("Angry")){
					count += 1;
				}
			}
			Integer dialogue_len = end-start-1;
			//输出所有的dia_score值，最后一个对话输不出来。改进！！
			if (dialogue_len > 0) {
				float dia_score = count/dialogue_len;                      //是情绪值。不能简单等于置信度。
				testwriter.write(resultlist.get(start).trim() + '\t' + String.valueOf(dia_score) + "\n");
			}
/*			if (dia_score > 0.3) {
//				System.out.println(txt_speech_result.get(start).trim() + '\t' + "Angry");
				testwriter.write(newresultlist.get(start).trim() + '\t' + "Angry" + '\t' + String.valueOf(dia_score) + "\n");
			}
			else {
				testwriter.write(newresultlist.get(start).trim() + '\t' + "Neutral" + '\t' + String.valueOf(dia_score) + "\n");
			}
			start = 0;
			end = 0;
			//****考虑特殊情况
			if(j==wavname_ls.size()-2){
//				System.out.println("j:"+j);
				int m=wavname_ls.get(j+1);
				count=0;
				for(int k= m+1; k<newresultlist.size(); k++){
					String label = newresultlist.get(k).split("\t")[4];
					if (label.contains("Angry")){
						count += 1;
					}
				}
				dia_score = count/(newresultlist.size()-m);
				if (dia_score > 0.1) {
//					System.out.println(txt_speech_result.get(start).trim() + '\t' + "Angry");
					testwriter.write(newresultlist.get(m).trim() + '\t' + "Angry" + '\t' + String.valueOf(dia_score) + "\n");
				}
				else {
					testwriter.write(newresultlist.get(m).trim() + '\t' + "Neutral" + '\t' + String.valueOf(dia_score) + "\n");
				}				
			}*/
		}		
		testwriter.close();
	}
		
	public void emotionDetect(Integer treadcount, String path, String txtresult, String diaoutput) throws Exception{

		readDic();
		readFile(path);////选择对话标题特征
		FormateData();             
		Predict(treadcount);
		conbineTxtResult(path);////选择对话标题特征
		addLingRule(txtresult);       //加入语言规则
//		dialogue_score(diaoutput);////选择对话标题特征
		//System.out.println("combine segment result:	" + outpath);
		System.out.println("dialogue result: " + diaoutput);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub		
		try {
			long starttime = System.currentTimeMillis();
			EmoDetect_m3 ed = new EmoDetect_m3();
			ed.emotionDetect(2, "D:\\workspace_eclipse\\Emotion_Txt_Speech\\data_chinamobile\\pre_test_dia\\sentence.test.data","D:\\workspace_eclipse\\Emotion_Txt_Speech\\data_chinamobile\\pre_test_dia\\test_txt_emotion","D:\\workspace_eclipse\\Emotion_Txt_Speech\\data_chinamobile\\pre_test_dia\\dialogue_emotion");
//			ed.emotionDetect(2, "newdata\\text_role_result.txt","newdata\\test_txt_emotion","newdata\\dialogue_emotion");
//			ed.emotionDetect(Integer.valueOf(args[0]),args[1],args[2],args[3],args[4]);
			long endtime = System.currentTimeMillis();
			System.err.println(endtime - starttime);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
/*	public static void main(String[] args) {
		// TODO Auto-generated method stub		
		try {
			long starttime = System.currentTimeMillis();
			EmoDetect_m3 ed = new EmoDetect_m3();
//			ed.emotionDetect(2, "data\\data_all","data\\test_txt_emotion","data\\dialogue_emotion");
			Integer threadcount=Integer.parseInt(args[0]);
			String data_input=args[1];
			String sen_resultString=args[2];
			String dialogue_emotion=args[3];
			ed.emotionDetect(threadcount, data_input,sen_resultString,dialogue_emotion);
//			ed.emotionDetect(Integer.valueOf(args[0]),args[1],args[2],args[3],args[4]);
			long endtime = System.currentTimeMillis();
			System.err.println(endtime - starttime);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}*/
}
